﻿#include "load3ds.h"
#include <QtDebug>
#include <QFile>

QString SetLine(char* sPrm);

//#include "Vlog.h"

//CVlog flog("f3ds.log");

void CMaterial::LoadTexture(QString Path)
{
    if(mapn[0] == 0) return; // No texture
    QString str(Path);
    QImage  imgb;
    str+= mapn;
    imgb.load(str);
    if(imgb.isNull())
        qDebug() << "read file ERROR " << str;
    else {
//        qDebug() << "read image" << str << imgb.width() << imgb.height();
        img = imgb.mirrored(false, true);
    }
}
void CFile3ds::LoadTextures(QString Path)
{
    if (pMat == NULL) return;
    LPMATERIAL plist = pMat;
    while (plist) {
        plist->LoadTexture(Path);
        plist = plist->pnext;
    }
}
bool CFile3ds::SetBoundBox(void)
{
	if(pbound) { delete [] pbound;  pbound = NULL; }
	if(pMesh == NULL) return false;
	pbound = new Vertex[2];
	if (pbound == NULL) return false;
	pbound[0].X = pbound[0].Y = pbound[0].Z = pbound[1].X = pbound[1].Y = pbound[1].Z = 0.f;
	LPMESH pmh = pMesh;
	LPVERTEX	pbnd = new Vertex[2];
	if (pbnd == NULL) return false;
	bool bfirst = true;
	while(pmh) { // Расчитать границы всех mesh
		if (pmh->GetBoundBox(pbnd)) {
			if (bfirst) {
				pbound[0].X = pbnd[0].X;
				pbound[0].Y = pbnd[0].Y;
				pbound[0].Z = pbnd[0].Z;
				pbound[1].X = pbnd[1].X;
				pbound[1].Y = pbnd[1].Y;
				pbound[1].Z = pbnd[1].Z;
				bfirst = false;
			}
			else {
				if (pbnd[0].X < pbound[0].X) pbound[0].X = pbnd[0].X;
				if (pbnd[1].X > pbound[1].X) pbound[1].X = pbnd[1].X;
				if (pbnd[0].Y < pbound[0].Y) pbound[0].Y = pbnd[0].Y;
				if (pbnd[1].Y > pbound[1].Y) pbound[1].Y = pbnd[1].Y;
				if (pbnd[0].Z < pbound[0].Z) pbound[0].Z = pbnd[0].Z;
				if (pbnd[1].Z > pbound[1].Z) pbound[1].Z = pbnd[1].Z;
			}
		}
		pmh = pmh->pnext;
	}
	delete[] pbnd;
	return true;
}
/*
void CFile3ds::VlogReport(void)
{
	flog.log("\nReport\n");
	flog.log("**********************************************\n");
	if (pbound) flog.log("Bounds: (X %.2f Y %.2f Z %.2f),(X %.2f Y %.2f Z %.2f)\n",
		pbound[0].X,pbound[0].Y,pbound[0].Z,pbound[1].X,pbound[1].Y,pbound[1].Z);
	LPMATERIAL pmt = pMat;
	flog.log("\nList of materials\n");
	while(pmt) {
		flog.log("%s\n", pmt->name);
		pmt->VlogMaterial();
		pmt = pmt->pnext;
	}
	LPMESH pms = pMesh;
	flog.log("\nList of meshes\n");
	while(pms) {
		flog.log("%s\n", pms->name);
		pms->VlogMesh();
		pms = pms->pnext;
	}
#ifdef USEFRAME
	LPFRAME pmf = pFrame;
	flog.log("List of frames\n");
	while(pmf) {
		flog.log("ID %d %s\n", pmf->id, pmf->name);
		pmf->VlogFrame();
		pmf = pmf->GetNext();
	}
#endif
	flog.close();
}
void CMesh::VlogMesh(void)
{
	//for(unsigned short i = 0; i < nvert; i++) {
	//	flog.log("\t%d (X%.3f Y%.3f Z%.3f)\n", i, pvert[i].X, pvert[i].Y, pvert[i].Z);
	//}
	if(pmatrix) {
		flog.log("\tLocal matrix\n");
   //The three first blocks of three floats are the definition
   //(in the absolute axis) of the local axis X Y Z of the object.
   //And the last block of three floats is the local center of the object.
		flog.log("\t(X %.2f Y %.2f Z %.2f),(X %.2f Y %.2f Z %.2f),(X %.2f Y%. 2f Z %.2f),(X %.3f Y %.3f Z %.2f)\n",
			pmatrix[0], pmatrix[1], pmatrix[2], pmatrix[3], pmatrix[4], pmatrix[5],
			pmatrix[6], pmatrix[7], pmatrix[8], pmatrix[9], pmatrix[10], pmatrix[11]);
	}
	LPVERTEX	pbnd = new Vertex[2];
	if (pbnd) {
		if(GetBoundBox(pbnd))
		flog.log("Bounds: (X %.2f Y %.2f Z %.2f),(X %.2f Y %.2f Z %.2f)\n",
			pbnd[0].X, pbnd[0].Y, pbnd[0].Z, pbnd[1].X, pbnd[1].Y, pbnd[1].Z);
		delete[] pbnd;
	}
	if (pvert)	{	// Массив вершин (x,y,z)
		flog.log("\tVertexes (%d)\n", nvert);
	}
	if (pface) {
		flog.log("\tFace list (%d)\n", nface);
	}
	LPMSHMAT pmt = pmshmat;
	while(pmt) {
		flog.log("\tMaterial %s (faces %d)\n", pmt->name, pmt->nface);
		pmt = pmt->pnext;
	}
}
void CMaterial::VlogMaterial(void)
{
	if(pamb) flog.log("\tR%3dG%3dB%3d Ambient\n", pamb->R, pamb->G, pamb->B);
	if(pdif) flog.log("\tR%3dG%3dB%3d Diffuse\n", pdif->R, pdif->G, pdif->B);
	if(pspc) flog.log("\tR%3dG%3dB%3d Specular\n", pspc->R, pspc->G, pspc->B);
	if(mapn[0] != 0) flog.log("\tTexture name %s\n", mapn);
}
*/
static const char *DummyName3ds = "$$$DUMMY";
bool IsDummyObject(char* name) { if(strncmp(name,DummyName3ds,strlen(DummyName3ds)) == 0) return true; return false; }

bool CFile3ds::Load3ds (QString FileName)
{

    Release();

	LPMATERIAL	pmat			= NULL;
	LPMESH			pmesh		= NULL;
	LPMSHMAT	pmshmat	= NULL;
#ifdef USEFRAME
	LPFRAME		pframe		= NULL; // Текущий фрейм
    LPFRAME		pframe1	= NULL; // Предыдущий фрейм
	Vertex	vert;
	float		fp;
    unsigned int	ulp, ulp1, n;
#endif
	LPCOLOR24	pcolor		= NULL;
	unsigned short	l_chunk_id;		// Chunk identifier
	unsigned int		l_chunk_lenght;	// Chunk lenght
	unsigned char	l_char;				// Char variable
	unsigned short l_qty; //Number of elements in each chunk

    char	name[24];
	FILE*	l_file;
    int		i;

    l_file = fopen((FileName.toLatin1()).data(), "rb"); //Open the file
    if(l_file == NULL) return false; //Open the file

    QString str = FileName;
    str.replace(str.count() - 3, 3, "log");
    QFile file(str);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) return false;
    QTextStream out(&file);
    bool bKey = false;
	while(1) //Loop to scan the whole file 
	{

		if(fread(&l_chunk_id, 2, 1, l_file) == 0) break; //Read the chunk header
		fread (&l_chunk_lenght, 4, 1, l_file); //Read the lenght of the chunk

        if(bKey) {
            str = "Chunk ID: 0x"; out << str  << QString::number(l_chunk_id, 16).toUpper();
            str = " Lengh ="; out << str  << l_chunk_lenght << "\n";
        }

		switch (l_chunk_id)
        {
			//----------------- M3DMAGIC 0x4D4D -------------------------
			// The M3DMAGIC chunk is used at the beginning of a .3ds file
			//-----------------------------------------------------------
			case 0x4d4d: 
				//flog.log("Beginning of a file M3DMAGIC 0x4D4D\n");
				break;
			//----------------- MDATA 0x3D3D -----------------
			// Mesh section with a length dependent upon the chunks contained
			// within it. Can appear only once and is required.
			//-------------------------------------------
			case 0x3d3d:
//				flog.log("\nMesh section MDATA 0x3D3D\n");
//				flog.log("***************************");
				break;
			//----------------- KFDATA 0xB000 -----------------
			// Keyframe definition section with a length dependent upon the chunks
			// contained within it. Can appear only once. 
			//-------------------------------------------
			case 0xb000:
            bKey = true;
            out << "\nKeyframe definition section KFDATA 0xB000\n";
//				flog.log("\nKeyframe definition section KFDATA 0xB000\n");
//				flog.log("***************************************\n");
				break;
			//----------------- OBJECT_NODE_TAG 0xB002 -----------------
			// Keyframe animation for mesh objects with a length dependent upon the chunks
			// contained within it. It may appear as many times as needed.
			//-------------------------------------------
			case 0xb002:
            out <<"Keyframe animation for mesh objects OBJECT_NODE_TAG 0xB002\n";
//				flog.log("\nKeyframe animation for mesh objects OBJECT_NODE_TAG 0xB002\n");
				break;
        case 0xb030:
            out << "NODE_ID (0xB030):" << "\n";
            fread (name, 2, 1, l_file);
            break;
        case 0xb010:
            i = 0;
            do {
                fread (&l_char, 1, 1, l_file);
                name[i++] = l_char;
            } while(l_char != '\0' && i < 11);
            if(i == 11) name[10] = '\0';
            str = name;
            out << "NODE_HDR (0xB010): " << str << "\n";
            fseek(l_file, 6, SEEK_CUR);
            break;
        case 0xb013:
        {
            float x,y,z;
             fread (&x, 4, 1, l_file);
             fread (&y, 4, 1, l_file);
             fread (&z, 4, 1, l_file);
            out << "PIVOT (0xB013):" << x << "," << y << "," << z  << "\n";
//            LPMESH pmesh = GetMesh(name);
//            for (i = 0; i < pmesh->nvert; i++) { // Проходим через все вершины
//                pmesh->pvert[i].X -= x;
//                pmesh->pvert[i].Y -= y;
//                pmesh->pvert[i].Z -= z;
//            }
        }
            break;
        case 0xb014:
        {
            float x,y,z;
             fread (&x, 4, 1, l_file);
             fread (&y, 4, 1, l_file);
             fread (&z, 4, 1, l_file);
            out << "BOUNDBOX (0xB014):" << x << "," << y << "," << z  << "/";
            fread (&x, 4, 1, l_file);
            fread (&y, 4, 1, l_file);
            fread (&z, 4, 1, l_file);
            out << x << "," << y << "," << z  << "\n";
        }
            break;
#ifdef USEFRAME
            //----------------- NODE_ID 0xB030 -----------------
			// Node ordering information. Node order is used to identify the hierarchical parents.
			//-------------------------------------------
			case 0xb030:
				fread(&l_qty, sizeof(unsigned short), 1, l_file);
				if (pframe == NULL) {  // Первый frame
					pFrame = new CFrame;
					pFrame->id = l_qty;
				}
				else {
					pframe = new CFrame;
					pframe->id = l_qty;
				}
				flog.log("Node ordering information (%d) NODE_ID 0xB030 \n", l_qty);
				break;
			//--------------- NODE_HDR 0xB010 ---------------
			// General keyframe object information with a length of 16 to 2 (NULL+1-10 chars+unsigned short*2+short).
			//-------------------------------------------
			case 0xb010: 
				i = 0;
				do {
					fread (&l_char, 1, 1, l_file);
                    name[i++] = l_char;
                } while(l_char != '\0' && i < 11);
				if (i == 11) name[10] = '\0';
				fread (&l_qty, sizeof(unsigned short), 1, l_file); // Flag 1
				fread (&l_qty, sizeof(unsigned short), 1, l_file); // Flag 2
				fread (&l_qty, sizeof(unsigned short), 1, l_file); // Parent index
				if (pframe == NULL) {  // Первый frame
					pframe = pFrame;
				}
				else { // Look for hierarchy of frames
					if (l_qty == 0xFFFF)  pFrame->AddNext(pframe); // No parent
					else {
						pframe1 = pFrame->FindFrame(l_qty);
						if (pframe1) pframe1->AddChild(pframe);
						else pFrame->AddNext(pframe);
					}
				}
				strcat_s(pframe->name, 11, name);
				flog.log("Keyframe Mesh %s , Parent index %d, Node order %d\n", pframe->name, l_qty, pframe->id);
				break;
			//----------------- PIVOT 0xB013 -----------------
			// Rotational pivoting point and scaling center reference with a length of 18 (point+header).
			// Can appear only once per OBJECT_NODE_TAG and is required.
			//-------------------------------------------
			case 0xb013:
				fread(&pframe->pivot, sizeof(Vertex), 1, l_file);
				flog.log("Rotational pivoting point (%f, %f, %f) PIVOT 0xB013\n", pframe->pivot.X, pframe->pivot.Y, pframe->pivot.Z);
			break;
			//----------------- POS_TRACK_TAG 0xB020 -----------------
			// Position keys with a length that varies depending on the number of keys and the variably
			// sized keyheaders. Can appear only once per node and is required. 
			//-------------------------------------------
			case 0xb020:
				fread(&l_qty, sizeof(unsigned short), 1, l_file);	// trackheader Flags
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// number of keys in a track
				for(n = 0; n < ulp; n++) {
					fread(&ulp1, sizeof(unsigned int), 1, l_file);	// the frame number where the key occurs
					fread(&l_qty, sizeof(unsigned short), 1, l_file);	// A set of bits that determine which spline terms are present
					if(l_qty & 0x01) fread(&fp, sizeof(float), 1, l_file); // Spline tension value ranges from –1.0 to 1.0
					if(l_qty & 0x02) fread(&fp, sizeof(float), 1, l_file); // Spline continuity value ranges from –1.0 to 1.0
					if(l_qty & 0x04) fread(&fp, sizeof(float), 1, l_file); // Spline bias value ranges from –1.0 to 1.0
					if(l_qty & 0x08) fread(&fp, sizeof(float), 1, l_file); // Key ease to value ranges from –1.0 to 1.0
					if(l_qty & 0x10) fread(&fp, sizeof(float), 1, l_file); // Key ease from value ranges from –1.0 to 1.0
					if(n == 0) // Запоминаем первый key position
						fread(&pframe->ptrack, sizeof(Vertex), 1, l_file);
					else
						fread(&vert, sizeof(Vertex), 1, l_file);
				}
				if (ulp == 1) flog.log("Position key (%.2f, %.2f, %.2f,) POS_TRACK_TAG 0xB020\n", pframe->ptrack.X, pframe->ptrack.Y, pframe->ptrack.Z);
				else flog.log("Position keys (number of keys in a track %d) POS_TRACK_TAG 0xB020\n", ulp);
				break;
			//----------------- ROT_TRACK_TAG 0xB021 -----------------
			// Mesh object rotation keys with a length that varies depending on the number
			// of keys and the variably sized keyheaders.
			//-------------------------------------------
			case 0xb021:
				fread(&l_qty, sizeof(unsigned short), 1, l_file);	// trackheader Flags
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// number of keys in a track
				for(n = 0; n < ulp; n++) {
					fread(&ulp1, sizeof(unsigned int), 1, l_file);	// the frame number where the key occurs
					fread(&l_qty, sizeof(unsigned short), 1, l_file);	// A set of bits that determine which spline terms are present
					if(l_qty & 0x01) fread(&fp, sizeof(float), 1, l_file); // Spline tension value ranges from –1.0 to 1.0
					if(l_qty & 0x02) fread(&fp, sizeof(float), 1, l_file); // Spline continuity value ranges from –1.0 to 1.0
					if(l_qty & 0x04) fread(&fp, sizeof(float), 1, l_file); // Spline bias value ranges from –1.0 to 1.0
					if(l_qty & 0x08) fread(&fp, sizeof(float), 1, l_file); // Key ease to value ranges from –1.0 to 1.0
					if(l_qty & 0x10) fread(&fp, sizeof(float), 1, l_file); // Key ease from value ranges from –1.0 to 1.0
					if(n == 0) { // Запоминаем первый key position
						fread(&pframe->rangle, sizeof(float), 1, l_file);
						fread(&pframe->rtrack, sizeof(Vertex), 1, l_file);
					}
					else {
						fread(&fp, sizeof(float), 1, l_file);
						fread(&vert, sizeof(Vertex), 1, l_file);
					}
				}
				if (ulp == 1) flog.log("Rotation key %.2f (%.2f, %.2f, %.2f,) ROT_TRACK_TAG 0xB021\n", pframe->rangle, pframe->rtrack.X, pframe->rtrack.Y, pframe->rtrack.Z);
				else flog.log("Rotation keys (number of keys in a track %d) ROT_TRACK_TAG 0xB021\n", ulp);
				break;
			//----------------- SCL_TRACK_TAG 0xB022 -----------------
			// Mesh object scaling keys with a length that varies depending on the number
			// of keys and the variably sized keyheaders.
			//-------------------------------------------
			case 0xb022:
				fread(&l_qty, sizeof(unsigned short), 1, l_file);	// trackheader Flags
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// not used
				fread(&ulp, sizeof(unsigned int), 1, l_file);		// number of keys in a track
				for(n = 0; n < ulp; n++) {
					fread(&ulp1, sizeof(unsigned int), 1, l_file);	// the frame number where the key occurs
					fread(&l_qty, sizeof(unsigned short), 1, l_file);	// A set of bits that determine which spline terms are present
					if(l_qty & 0x01) fread(&fp, sizeof(float), 1, l_file); // Spline tension value ranges from –1.0 to 1.0
					if(l_qty & 0x02) fread(&fp, sizeof(float), 1, l_file); // Spline continuity value ranges from –1.0 to 1.0
					if(l_qty & 0x04) fread(&fp, sizeof(float), 1, l_file); // Spline bias value ranges from –1.0 to 1.0
					if(l_qty & 0x08) fread(&fp, sizeof(float), 1, l_file); // Key ease to value ranges from –1.0 to 1.0
					if(l_qty & 0x10) fread(&fp, sizeof(float), 1, l_file); // Key ease from value ranges from –1.0 to 1.0
					if(n == 0) // Запоминаем первый key position
						fread(&pframe->strack, sizeof(Vertex), 1, l_file);
					else
						fread(&vert, sizeof(Vertex), 1, l_file);
				}
				if (ulp == 1) flog.log("Scaling key (%.2f, %.2f, %.2f,) SCL_TRACK_TAG 0xB022\n", pframe->strack.X, pframe->strack.Y, pframe->strack.Z);
				else flog.log("Scaling keys (number of keys in a track %d) SCL_TRACK_TAG 0xB022\n", ulp);
				break;
			//----------------- BOUNDBOX 0xB014 -----------------
			// Mesh object bounding box definition with a length of 30 (point*2+header). 
			// Can appear only once per OBJECT_NODE_TAG and is required.
			//-------------------------------------------
			case 0xb014:
				pframe->pbound = new Vertex[2];
				fread(pframe->pbound, sizeof(Vertex)*2, 1, l_file);
				flog.log("Mesh object bounding box (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) \n",
					pframe->pbound[0].X, pframe->pbound[0].Y, pframe->pbound[0].Z, 
					pframe->pbound[1].X, pframe->pbound[1].Y, pframe->pbound[1].Z);
			break;
#endif // USEFRAME
			//--------------- MAT_ENTRY 0xAFFF ---------------
			// Material definition entry with a length dependent upon the chunks used.
			// Can appear as many times as needed as int as each MAT_NAME remains unique.
            // Material definitions must appear in the file before the objects that use them.
			case 0xafff:
				if(pmat == NULL) {
					pMat = new CMaterial; // Первый материал
					pmat = pMat;
				}
				else {
					pmat = new CMaterial;
					pMat->AddNext(pmat);
				}
//				flog.log("\nMaterial definition entry MAT_ENTRY 0xAFFF\n");
				break;
			//--------------- MAT_NAME 0xA000 ---------------
			// Name of material definition with a length of 8 - 22
			//(1 NULL+1 to 15 chars+ header). Must appear with MAT_ENTRY
			// and can appear only once per MAT_ENTRY.
			//-------------------------------------------
			case 0xa000:
                i = 0;
//                for(i = 0; i < l_chunk_lenght - 6; i++)
//                    fread(&pmat->name[i], 1, 1, l_file);
//                pmat->name[i] = 0;
                do {
                    fread(&l_char, 1, 1, l_file);
                    pmat->name[i++] = l_char;
                } while(l_char != '\0' && i < 17);
                if(i == 17) pmat->name[16] = '\0';
                str = "Name of material definition: "; out << str  << SetLine(pmat->name) << "\n";
                //flog.log("Name of material definition (%s) MAT_NAME 0xA000\n", pmat->name);
				break;
			//----------------- MAT_AMBIENT 0xA010 -----------------
			// Ambient material color with a length of 15 (COLOR_24+header) or 
			// 24 (COLOR_24+LIN_COLOR_24+header). 
			// Must appear with MAT_ENTRY and can appear only once per MAT_ENTRY. 
			//-------------------------------------------
			case 0xa010:
				pmat->pamb = new Color24;
				pcolor = pmat->pamb;
//				flog.log("Ambient material color MAT_AMBIENT 0xA010\n");
				break;
			//----------------- MAT_DIFFUSE 0xA020 -----------------
			// Diffuse material color with a length of 15 (COLOR_24+header) or 
			// 24 (COLOR_24+LIN_COLOR_24+header). 
			// Must appear with MAT_ENTRY and can appear only once per MAT_ENTRY. 
			//-------------------------------------------
			case 0xa020:
				pmat->pdif = new Color24;
				pcolor = pmat->pdif;
//				flog.log("Diffuse material color MAT_DIFFUSE 0xA020\n");
				break;
			//----------------- MAT_SPECULAR 0xA030 -----------------
			// Specular material color with a length of 15 (COLOR_24+header) or 
			// 24 (COLOR_24+LIN_COLOR_24+header). 
			// Must appear with MAT_ENTRY and can appear only once per MAT_ENTRY. 
			//-------------------------------------------
			case 0xa030:
				pmat->pspc = new Color24;
				pcolor = pmat->pspc;
//				flog.log("Specular material color MAT_SPECULAR 0xA030\n");
				break;
			//----------------- COLOR_24 0x0011 -----------------
			// An integer definition of color with a length of 9 (3*unsigned byte+header). 
			// In many places, Release 3 has replaced this chunk with LIN_COLOR_24. 
			// In those places, Release 3 will write a COLOR_24 chunk for backwards compatibility with Releases 1 and 2. 
			//-------------------------------------------
			case 0x0011:
				fread(pcolor, sizeof(unsigned char)*3, 1, l_file);
//				flog.log("Color RGB (%d, %d, %d) COLOR_24 0x0011\n", pcolor->R, pcolor->G, pcolor->B);
				break;
			//----------------- LIN_COLOR_24 0x0012 -----------------
			// An integer definition of linear color with a length of 9 (3*unsigned byte+header). 
			// In many places, this chunk is used by 3D Studio Release 3 instead of the gamma-corrected Color_24 chunk.
			//-------------------------------------------
			case 0x0012:
				fread(pcolor, sizeof(unsigned char)*3, 1, l_file);
//				flog.log("Color RGB (%d, %d, %d) LIN_COLOR_24 0x0012\n", pcolor->R, pcolor->G, pcolor->B);
				break;
			//----------------- MAT_TEXMAP 0xA200 -----------------
			// Material texture map definition with a length dependent upon the chunks contained within it.
			// Can appear only once per MAT_ENTRY but is not required.
			//-------------------------------------------
			case 0xa200:
//				flog.log("Material texture map definition MAT_TEXMAP 0xA200\n");
				break;
			//--------------- MAT_MAPNAME 0xA300 ---------------
			// A string definition of a bitmap filename (Twelve characters plus NULL).
			//-------------------------------------------
			case 0xa300:
				i = 0;
				do {
					fread(&l_char, 1, 1, l_file);
                    pmat->mapn[i++] = l_char;
                } while(l_char != '\0' && i < 13);
				if(i == 13) pmat->mapn[12] = '\0';
                str = "Bitmap filename: "; out << str  << SetLine(pmat->mapn) << "\n";
//				flog.log("Bitmap filename (%s) MAT_MAPNAME 0xA300\n", pmat->mapn);
				break;
			//--------------- NAMED_OBJECT 0x4000 ---------------
			// These chunks define the various objects in a scene, lights, cameras, and meshes. 
			//-------------------------------------------
			case 0x4000: 
				i = 0;
				do {
					fread (&l_char, 1, 1, l_file);
                    name[i++] = l_char;
                } while(l_char != '\0' && i < 11);
				if(i == 11) name[10] = '\0';
//				flog.log("\nNamed object (%s) NAMED_OBJECT 0x4000\n", name);
				break;
			//--------------- N_TRI_OBJECT 0x4100 ---------------
			// Mesh object definition with a length dependent upon the subchunks used.
			// Can appear only once per NAMED_OBJECT, and cannot be used if N_DIRECT_LIGHT
			// or N_CAMERA have been defined in NAMED_OBJECT.
			//-------------------------------------------
			case 0x4100:
				if(pmesh == NULL) {
					pMesh = new CMesh;  // Первый графический объект
					pmesh = pMesh;
				}
				else {
					pmesh = new CMesh;
					pMesh->AddNext(pmesh);
				}
				i = 0;
				do pmesh->name[i] = name[i]; while(name[i++] != '\0' && i < 11);
//				flog.log("Mesh object N_TRI_OBJECT 0x4100\n");
			break;
			//--------------- POINT_ARRAY 0x4110  ---------------
			// A list of vertices used to construct the mesh with a length based on 8+9*count (header+unsigned short+point*count). 
			//-------------------------------------------
			case 0x4110: 
				fread (&pmesh->nvert, sizeof(unsigned short), 1, l_file);
				pmesh->pvert = new Vertex[pmesh->nvert];
				fread (pmesh->pvert, sizeof(Vertex)*pmesh->nvert, 1, l_file);

//				// Теперь все вершины прочитаны. Так как в моделях 3DS Max всегда перевёрнуты
//				// оси, нужно поменять Y-значения и Z-значения наших вершин.
                for (i = 0; i < pmesh->nvert; i++) { // Проходим через все вершины и меняем y<->z
                    float fTempY = pmesh->pvert[i].Y; // Сохраняем старое знач-е Y
                    pmesh->pvert[i].Y = pmesh->pvert[i].Z; // Устанавливаем значение Y в Z
//                    // Устанавливаем значение Z в Y и делаем его отрицательным, т.к. в 3ds max Z-ось перевернута
//                    pmesh->pvert[i].Z = -fTempY;
                    pmesh->pvert[i].Z = fTempY;
                }

//				flog.log("A list of vertices (%d) POINT_ARRAY 0x4110\n", pmesh->nvert);
				break;
			//--------------- FACE_ARRAY 0x4120 ----------------
			// A list of faces that construct the mesh with a length based on 8+8*count
			//(header+unsigned short+ face*count) plus the lengths of MSH_MAT_GROUP and 
			// SMOOTH_GROUP. Can appear only once per N_TRI_OBJECT.
			//-------------------------------------------
			case 0x4120:
				fread (&pmesh->nface, sizeof (unsigned short), 1, l_file);
				pmesh->pface = new Face[pmesh->nface];
				fread (pmesh->pface, sizeof(Face)*pmesh->nface, 1, l_file);
//				flog.log("A list of faces (%d) FACE_ARRAY 0x4120\n", pmesh->nface);
				//for (i = 0; i < pmesh->nface; i++) flog.log("Face %d A%d B%d C%d\n", i + 1, pmesh->pface[i].A, pmesh->pface[i].B, pmesh->pface[i].C);
				break;
			//------------- MSH_MAT_GROUP 0x4130 ------------
			// A list of faces that are assigned the material named. It has a length based 
			// on 8 (header+unsigned short), plus 2 to 16 (1 NULL+n chars), plus 2*count (unsigned short*count).
			// May appear as many times as there are materials defined in the object but is not required.
			//-------------------------------------------
			case 0x4130:
				if(pmesh->pmshmat == NULL) {
					pmesh->pmshmat = new CMshMat;
					pmshmat = pmesh->pmshmat;
				}
				else {
					pmshmat = new CMshMat;
					pmesh->pmshmat->AddNext(pmshmat);
				}
				i = 0;
				do { // Читаем имя материала
					fread(&l_char, 1, 1, l_file);
                    pmshmat->name[i++] = l_char;
                } while(l_char != '\0' && i < 17);
				if(i == 17) pmshmat->name[16] = '\0';
				fread (&pmshmat->nface, sizeof (unsigned short), 1, l_file); // Number of faces assigned material
				if(pmshmat->nface != 0) {
					pmshmat->pface = new unsigned short[pmshmat->nface];
					fread (pmshmat->pface, sizeof(unsigned short)*pmshmat->nface, 1, l_file);
				}
//				flog.log("A list of faces (%d) that are assigned the material named (%s) MSH_MAT_GROUP 0x4130\n", pmshmat->nface, pmshmat->name);
				//for (i = 0; i < pmshmat->nface; i++) flog.log("%d ", pmshmat->pface[i]); flog.log("\n");
				break;
			//------------- TEX_VERTS 0x4140 ------------
			// Texture coordinate values with a length based on the number of vertices
			// in POINT_ARRAY*8 + 8 (count*texvert+header+unsigned short). Can appear only once
			// per N_TRI_OBJECT but is not required.
			//-------------------------------------------
			case 0x4140:
				fread (&l_qty, sizeof (unsigned short), 1, l_file);
				pmesh->ptvert = new TexVert[pmesh->nvert];
				fread (pmesh->ptvert, sizeof(TexVert)*pmesh->nvert, 1, l_file);
//				flog.log("Texture coordinate values (%d) TEX_VERTS 0x4140\n", pmesh->nvert);
                break;
//#ifdef USEFRAME
				//------------- MESH_MATRIX 0x4160 ------------
			// Transformation matrix for N_TRI_OBJECT with a length of 54 (float*12+header).
			// Can appear only once per N_TRI_OBJECT but is not required. 
			//-------------------------------------------
			case 0x4160:
//				pmesh->pmatrix = new float[12];
//				fread (pmesh->pmatrix, sizeof(float)*12, 1, l_file);
//				flog.log("Transformation matrix MESH_MATRIX 0x4160\n");
//				break;
//#endif
/*
CHUNK_TRMATRIX (0x4160) содержит матрицу перевода объекта в некое абстрактное начальное состояние; в то, что было первоначально
        смоделировано. Требуется для корректного отображения анимации: дело в том, что все объекты в файле хранятся по
        состоянию на нулевой кадр, а анимация записана по отношению к "начальным" моделям. Т.е. при отображений нулевого кадра
        эта матрица не нужна, а при отображении всего набора кадров надо будет сначала перевести объекты в "начальное" состояние
        с помощью этой самой матрицы перевода и применять все преобразования (перемещения, повороты, etc) именно к "начальным" объектам.
        Для перевода объекта в "начальное" состояние надо сначала сдвинуть его назад, то есть на вектор -offset (НЕ на offset), а потом применить
        матрицу поворота rotmatr. Матрица записана построчно. Не забудьте - в ней y и z тоже везде обменены местами!
    float rotmatrix[3][3]; // матрица поворота
    vector offset;         // смещение начального объекта
            m11 m12 m13
    M = m21 m22 m23
            m31 m32 m33
    (x, y, z) * M = (x*m11+y*m21 + z*m31, x*m12 + y*m22 + z*m32, x*m13 + y*m23 + z*m33)
*/
            float m[9]; float offset[3]; float p[3];
            fread (m, sizeof(float)*9, 1, l_file);
            fread (offset, sizeof(float)*3, 1, l_file);
            str = pmesh->name;
            out <<  "CHUNK_TRMATRIX (0x4160): ("; out << str << "\n";
            for (i = 0; i < pmesh->nvert; i++) { // Проходим через все вершины
                p[0] =  pmesh->pvert[i].X; p[2] =  pmesh->pvert[i].Y; p[1] =  pmesh->pvert[i].Z;
                p[0] -= offset[0]; p[1] -= offset[1]; p[2] -= offset[2];
                pmesh->pvert[i].X = p[0]*m[0] + p[1]*m[2] + p[2]*m[1];
                pmesh->pvert[i].Z = p[0]*m[3] + p[1]*m[5] + p[2]*m[4];
                pmesh->pvert[i].Y = p[0]*m[6] + p[1]*m[8] + p[2]*m[7];
            }
            break;
            //----------- Skip unknow chunks ------------
			//We need to skip all the chunks that currently we don't use. We use the chunk lenght information to set the file pointer to the same level next chunk
			//-------------------------------------------
            case 0x4700: // OBJ_CAMERA
                fread (&cam.pos, sizeof (float), 3, l_file);
                fread (&cam.target, sizeof (float), 3, l_file);
                fread (&cam.rot, sizeof (float), 1, l_file);
                fread (&cam.focal, sizeof (float), 1, l_file);
//        {
//            float y = cam.pos[1]; cam.pos[1] = cam.pos[2]; cam.pos[2] = y;
//            y = cam.target[1]; cam.target[1] = cam.target[2]; cam.target[2] = y;
//        }
                str = "Camera position (X,Y, Z): ("; out << str  << cam.pos[0] << ", " << cam.pos[1] << ", "<< cam.pos[2] << ") " << "\n";
                str = "Target position (X,Y, Z): ("; out << str  << cam.target[0] << ", " << cam.target[1] << ", "<< cam.target[2] << ") " << "\n";
                str = "Camera rotation = "; out << str  << cam.rot << "\n";
                str = "Camera focal length = "; out << str  << cam.focal << "\n";

//                fseek(l_file, 32, SEEK_CUR);
            break;
            default:
				//flog.log("Unknow chunks (0x%X)\n", l_chunk_id);
				fseek(l_file, l_chunk_lenght - 6, SEEK_CUR);
        } 
	}
	fclose (l_file); // Closes the file stream

#ifdef USEFRAME
	if(pframe1) { // Set the last frame to hierarchy of frames
		if(pframe->id > pframe1->id) pframe1->AddChild(pframe);
		else {
			pframe1 = pFrame->FindFrame(pframe->id);
			pframe1->AddNext(pframe);
		}
	}
#endif
    SetBoundBox();
    Vertex v; double r;
    GetCenterBoundBox(&v, r);
    str = "Центр сферы (X,Y, Z): ("; out << str  << v.X << ", " << v.Y << ", "<< v.Z << ") " << "\n";
    str = "Радиус сферы = "; out << str  << r << "\n";
    file.close();
	return true; // Returns ok
}
//******************************************************
